package com.cloud.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.RedisHealthIndicator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.lang.reflect.Method;
import java.util.List;

@Configuration
@EnableCaching
//@MapperScan("com.cloud.data")
public class BaseRedisConfig extends CachingConfigurerSupport {
    @Value("${cn.cloud.server.redis.host}")
    protected String host;
    @Value("${cn.cloud.server.redis.port}")
    protected Integer port;
    @Value("${cn.cloud.server.redis.password}")
    protected String password;
    @Value("${cn.cloud.server.redis.database}")
    protected Integer database;
    @Value("${cn.cloud.server.redis.enableCluster}")
    protected Boolean enableCluster;
    @Value("${cn.cloud.server.redis.timeout}")
    protected Integer timeout;
    @Value("${cn.cloud.server.redis.defaultExpiration}")
    protected Integer defaultExpiration;
    @Value("${cn.cloud.server.redis.pool.max-active}")
    protected Integer maxActive;
    @Value("${cn.cloud.server.redis.pool.max-idle}")
    protected Integer maxIdle;
    @Value("${cn.cloud.server.redis.pool.min-idle}")
    protected Integer minIdle;
    @Value("${cn.cloud.server.redis.pool.max-wait}")
    protected Integer maxWait;
    /**
     * 集群配置属性
     */
    @Value("#{'${cn.cloud.server.redis.cluster.nodes}'.split(',')}")
    protected List<String> nodes;
    @Value("${cn.cloud.server.redis.cluster.max-redirects}")
    protected Integer maxRedirects;

    @Bean
    public KeyGenerator wiselyKeyGenerator(){
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        if(this.enableCluster) {
            //集群节点配置
            RedisClusterConfiguration configuration = new RedisClusterConfiguration(this.nodes);
            configuration.setMaxRedirects(this.maxRedirects);
            JedisConnectionFactory factory = new JedisConnectionFactory(configuration);
            factory.setTimeout(this.timeout);
            factory.setUsePool(true);
            factory.getPoolConfig().setMaxTotal(this.maxActive);
            factory.getPoolConfig().setMaxWaitMillis((long)this.maxWait);
            factory.getPoolConfig().setMaxIdle(this.maxIdle);
            factory.getPoolConfig().setMinIdle(this.minIdle);
            factory.getPoolConfig().setTestOnReturn(true);
            factory.getPoolConfig().setTestOnBorrow(true);
            factory.getPoolConfig().setTestOnCreate(true);
            return factory;
        } else {
            JedisConnectionFactory factory = new JedisConnectionFactory();
            factory.setHostName(this.host);
            factory.setPort(this.port);
            if(null != this.password) {
                factory.setPassword(this.password);
            }

            factory.setTimeout(this.timeout);
            factory.setDatabase(this.database);
            factory.setUsePool(true);
            factory.getPoolConfig().setMaxTotal(this.maxActive);
            factory.getPoolConfig().setMaxWaitMillis((long)this.maxWait);
            factory.getPoolConfig().setMaxIdle(this.maxIdle);
            factory.getPoolConfig().setMinIdle(this.minIdle);
            return factory;
        }
    }

    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //系统默认超时时间
        cacheManager.setDefaultExpiration(defaultExpiration);
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        //设置序列化工具，这样ReportBean不需要实现Serializable接口
        setSerializer(template);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public RedisHealthIndicator redisHealthIndicator(RedisConnectionFactory redisConnectionFactory){
        return new RedisHealthIndicator(redisConnectionFactory);
    }

    @SuppressWarnings("unchecked")
    private void setSerializer(StringRedisTemplate template) {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
    }
}